﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Formatters.Binary;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
using System.Text;
using dis.sys.hw1.ClientProxy;
using dis.sys.hw1.Contract;
using dis.sys.hw1.Model;

namespace dis.sys.hw1.Service
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class HotelRegistrationService : IRegistrationService
    {
        private readonly ServiceLog _log = new ServiceLog(typeof(HotelRegistrationService), true);

        public IDictionary<string, DelegateHotelServiceProxy> HotelServiceProxies { get; set; }

        private IDictionary<Tuple<long, long, double, double>, Tuple<DateTime, IList<Hotel>>> queryCache;

        public IDictionary<Tuple<long, long, double, double>, Tuple<DateTime, IList<Hotel>>> QueryCache
        {
            get
            {
                return queryCache;
            }
            set
            {
                queryCache = value;
            }
        }

        public HotelRegistrationService(IDictionary<string, DelegateHotelServiceProxy> hotelServiceProxies = null,
            IDictionary<Tuple<long, long, double, double>, Tuple<DateTime, IList<Hotel>>> queryCache = null)
        {
            HotelServiceProxies = hotelServiceProxies ?? new ConcurrentDictionary<string, DelegateHotelServiceProxy>();
            QueryCache = queryCache;
        }

        public bool IsUp()
        {
            return true;
        }

        #region Implementation of IRegistrationService

        public void Register(string allianceName, int port)
        {
            var address = (OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as
             RemoteEndpointMessageProperty).Address;

            if(address.StartsWith("::"))
            {
                address = "localhost";
            }

            var fullAddress = "http://" + address + ":" + port + "/Services/DelegateHotelService";
            var httpEndpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(IDelegateHotelService)),
                new BasicHttpBinding(), new EndpointAddress(fullAddress));
            _log.Info("Registered alliance '" + allianceName + "' with address: " + fullAddress);
            var proxy = new DelegateHotelServiceProxy(httpEndpoint);
            
            HotelServiceProxies[allianceName] = proxy;
        }

        public void Unregister(string allianceName)
        {
            if (HotelServiceProxies.ContainsKey(allianceName))
            {
                _log.Info("Unregistered alliance '" + allianceName + "'");
                HotelServiceProxies.Remove(allianceName);
            }
            else
            {
                var msg = "Alliance " + allianceName + " is not registered";
                _log.Error(msg);
                throw new FaultException(msg);
            }
        }

        #endregion

        public void InvalidateCacheLine(string hotelName, double stars, double rating)
        {
            foreach (var key in QueryCache.Keys.Where(x => x.Item3 <= stars && x.Item4 <= rating))
	        {
               QueryCache.Remove(key);
	        }

            foreach (var cacheLine in QueryCache.Where(l => l.Value.Item2.Any(h => h.Name.Equals(hotelName))))
            {
                QueryCache.Remove(cacheLine.Key);
            }
        }
    }
}
